#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>

/* Variáveis globais */
pthread_t than, thbn, thpn;
pthread_mutex_t mutex;
mpf_t an, bn, pn, an1, bn1, pn1, raiz_de_2, temp_an, temp_bn, temp_bn2, temp_pn;

/* funções que são realizadas pelas threads quando elas são criadas */
/* novo valor de an1 */
void *thread_an(){
    pthread_mutex_lock(&mutex);

    mpf_sqrt(temp_an,an);
    mpf_set(an1, temp_an);
    mpf_div_ui(temp_an, temp_an, 2);
    mpf_mul_ui(an1, an1, 2);
    mpf_ui_div(an1, 1, an1);
    mpf_add(an1, temp_an, an1);
    
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}

/* novo valor de bn1 */
void *thread_bn(){
    pthread_mutex_lock(&mutex);

    mpf_sqrt(temp_bn, temp_bn);
    mpf_add_ui(bn1, bn, 1);
    mpf_mul(temp_bn, temp_bn, bn1);
    mpf_add(bn1, temp_bn, bn);
    mpf_div(bn1, temp_bn, bn1);
    
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}

/* novo valor de pn1 */
void *thread_pn(){
    pthread_mutex_lock(&mutex);

    mpf_add_ui(temp_pn, an1, 1);
    mpf_add_ui(pn1, bn1, 1);
    mpf_div(temp_pn, temp_pn, pn1);
    mpf_mul(pn1, pn, bn1);
    mpf_mul(pn1, pn1, temp_pn);

    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}



int main(){
    /* Declaração de Variáveis */
    FILE *f;
    long int precisao = 33333;
    int n = 0, i;
    float j;
    clock_t inicio = 0, fim = 0;
	
    /* marca o clock de inicio do programa */
    inicio = clock();

    /* seta a precsão para 10 milhões de casas decimais */
    mpf_set_default_prec(precisao);

    /* inicialização das variáveis, todas assumem valor 0*/
    mpf_init(an);
    mpf_init(an1);
    mpf_init(bn);
    mpf_init(bn1);
    mpf_init(pn);
    mpf_init(pn1);
    mpf_init(temp_an);
    mpf_init(temp_bn);
    mpf_init(temp_bn2);
    mpf_init(temp_pn);
    mpf_init(raiz_de_2);

    /* insere os valores iniciais */
    mpf_sqrt_ui(raiz_de_2, 2);
    /* an = 2^(1/2) ) */
    mpf_sqrt(an, raiz_de_2);

    /* bn = 0 */
    mpf_set_d(bn, 0);

    /* pn = 2+2^(1/2) */
    mpf_add_ui(pn,raiz_de_2,2);


    /* realiza 22 interações, o que já da praticamente o mesmo resultado do gauus-legendre*/
    while(n <= 22){
	mpf_set(temp_bn,an);
	/* an1 = (an^(1/2) )/2 + 1/(2*an^(1/2)*/
        pthread_create(&than, NULL, thread_an, NULL);

        /* bn1 = ( (an^(1/2))*(1 + bn) )/(an + bn) */
        pthread_create(&thbn, NULL, thread_bn, NULL);

	pthread_join(than, NULL);
	pthread_join(thbn, NULL);

        /* pn1 = pn*bn1*( (1+an1)/(1+bn1) ) */
        pthread_create(&thpn, NULL, thread_pn, NULL);

	/* espera as threads terminarem */

        pthread_join(thpn, NULL);

        /* atualiza as variaveis Xn */
        mpf_set(an, an1);
        mpf_set(bn, bn1);
        mpf_set(pn, pn1);

        n++;
    }
    /* imprime pi */
    f = fopen("pi_borwein_paralelo.txt", "w");
    i = mpf_out_str(f,10, 0, pn1);
    
    /* marca o clock do fim do programa */
    fim = clock();
    j = (fim - inicio);
    /* imprime o numero de caracteres escritos e os segundos gastos */
    fprintf(f,"\n%d caracteres escritos\n%f segundos gastos", i, j/CLOCKS_PER_SEC);

    fclose(f);

    return 0;
}
